// P2P主机网络学习
package hosts

import (
	"context"
	"fmt"
	"log"

	"github.com/libp2p/go-libp2p"
	dht "github.com/libp2p/go-libp2p-kad-dht"
	"github.com/libp2p/go-libp2p/core/crypto"
	"github.com/libp2p/go-libp2p/core/host"
	"github.com/libp2p/go-libp2p/core/routing"
	"github.com/libp2p/go-libp2p/p2p/net/connmgr"
	noise "github.com/libp2p/go-libp2p/p2p/security/noise"
	libp2ptls "github.com/libp2p/go-libp2p/p2p/security/tls"
)

// 入口函数
func Init() {
	// 创建密钥
	priv, _, err := crypto.GenerateKeyPair(crypto.Ed25519, -1)
	if err != nil {
		panic(err)
	}

	var idht *dht.IpfsDHT

	// 创建连接管理器
	connManager, err := connmgr.NewConnManager(
		100, // 最低
		400, // 最高,
	)
	if err != nil {
		panic(err)
	}

	// 创建连接内容
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// 创建libp2p主机
	h, err := libp2p.New(
		// 使用我们生成的密钥对
		libp2p.Identity(priv),
		// 多个侦听地址
		libp2p.ListenAddrStrings(
			"/ip4/0.0.0.0/tcp/9000",
			"/ip4/0.0.0.0/udp/9000/quic-v1",
		),
		// 支持TLS连接
		libp2p.Security(libp2ptls.ID, libp2ptls.New),
		// 支持噪声连接
		libp2p.Security(noise.ID, noise.New),
		// 支持quic协议
		// 因quic-v1协议已注册，所以此处无需再次进行注册
		// libp2p.Transport(libp2pquic.NewTransport),
		// 支持任何其他默认传输（TCP）
		libp2p.DefaultTransports,
		// 通过附加一个连接管理器来防止我们的对等设备有过多的连接。
		libp2p.ConnectionManager(connManager),
		// 尝试使用uPNP为NAT主机打开端口
		libp2p.NATPortMap(),
		// 让此主机使用DHT查找其他主机
		libp2p.Routing(func(h host.Host) (routing.PeerRouting, error) {
			idht, err = dht.New(ctx, h)
			return idht, err
		}),
	)
	if err != nil {
		panic(err)
	}
	defer h.Close()
	fmt.Println("p2p网络主机创建完成", h.ID())
	for _, v := range h.Addrs() {
		log.Printf("运行 'go run main.go -d %s/p2p/%s'", v, h.ID())
	}
}
